package demo.gateway.filters;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import demo.common.auth.entity.Payload;
import demo.common.auth.entity.UserInfo;
import demo.common.auth.utils.JwtUtils;
import demo.common.exception.SnowException;
import demo.common.exception.SnowExceptionEnum;
import demo.common.utils.CookieUtils;
import demo.gateway.config.FilterProperties;
import demo.gateway.config.JwtProperties;
import demo.user.client.UserClient;
import demo.user.dto.MenuDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

import java.awt.*;
import java.util.List;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.FORM_BODY_WRAPPER_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 网关鉴权过滤器
 */
@Slf4j
@Component
public class AuthFilter extends ZuulFilter {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtProperties properties;

    @Autowired
    private FilterProperties filterProperties;

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FORM_BODY_WRAPPER_FILTER_ORDER + 1;
    }

    @Override
    public boolean shouldFilter() {
        //获取请求对象
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        //获取请求路径
        String uri = request.getRequestURI();
        //获取放行白名单
        List<String> allowPaths = filterProperties.getAllowPaths();
        for (String allowPath : allowPaths) {
            if(uri.startsWith(allowPath)){
                //放行的白名单路径是不需要鉴权的，即：不需要进入到run方法执行，return false
                return false;
            }
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //获取请求对象
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        try {
            //微服务鉴权操作
            //1、获取请求数据中的token
            String token = CookieUtils.getCookieValue(request, properties.getUser().getCookieName());
            //2、解析token，获取用户角色
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, properties.getPublicKey(), UserInfo.class);
            // 缺少判断用户是否已经登出(可以直接调用验证模块里的verify)
//            Boolean boo = redisTemplate.hasKey(payload.getId());
//            if (boo) {
//                throw new SnowException(SnowExceptionEnum.UNAUTHORIZED);
//            }

            //3、基于用户角色，查询该用户具有的权限
            UserInfo userInfo = payload.getUserInfo();
            // 用户权限
            Long role = userInfo.getRole();
            //解析完token后，将user的id，放到请求头参数中，分发到微服务
            Long userId = userInfo.getId();
            ctx.addZuulRequestHeader("USER_ID",userId.toString());

            //获取用户请求路径
            String uri = request.getRequestURI();//例如：/api/auth/verify
            //获取用户请求方式
            String method = request.getMethod();
            MenuDTO menuDTO = userClient.getUserMenu(role, uri);
            if (menuDTO == null) {
                log.error("【网关中心】用户无权限");
                throw new SnowException(SnowExceptionEnum.USER_RIGHTS_INSUFFICIENT);
            }


//
//            //TODO 正常操作流程是要基于解析的用户角色，查询该用户具有的权限
//            //4、如果该用户具有访问某个微服务的权限，过滤器放行
//            //5、如果用户没有访问某个微服务的权限，则拦截请求
//            //获取用户请求路径
//            String uri = request.getRequestURI();//例如：/api/auth/verify
//            //request.getRequestURL(); //例如：http://api.leyou.com/api/auth/verify
//            //获取用户请求方式
//            String method = request.getMethod();
//            log.info("【网关】中，用户{}，具有的角色{}，通过{}请求方式，请求了{}路径",userInfo.getUsername(),role,method,uri);
        } catch (Exception e) {
            e.printStackTrace();
            //如果进入catch，说明token无效，都没有登录成功，不能放行，拦截请求
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;//return null 代表过滤器放行
    }
}
